#include "Interval.hpp"


std::ostream& operator<<(std::ostream& os, const Interval& r) {
	if (boost::numeric::empty(r))
		os << "Ø";
	else
		if (r.lower() == r.upper())
			os << "" << r.lower() << "";
		else
			os << "" << r.lower() << ".." << r.upper() << "";
	return os;
}

/**
 * True if i1 is subset_eq of i2
 */
bool interval_subset(Interval i1, Interval i2) 
{
	return boost::numeric::subset(i1, i2);
}

/*
 * If both are non-empty, we return an interval covering both i1 and i2.
 * If either i1 or i2 is empty, then we return the non-empty.
 * If both are empty then we return empty.
 */
Interval interval_union(Interval i1, Interval i2) 
{
	Interval ret = boost::numeric::hull(i1, i2);
	return ret;
}

/*
 * If i1 and i2 overlap, we return their intersection.
 * If i1 and i2 don't overlap, we return the empty-interval.
 */
Interval interval_intersection(Interval i1, Interval i2) 
{
	if (boost::numeric::empty(i1) && boost::numeric::empty(i2)) return Interval::empty();
	Interval ret = boost::numeric::intersect(i1, i2);
	return ret;
}

Interval interval_invert(Interval arg) {
	if (boost::numeric::empty(arg))								return Interval(-iint::inf(), iint::inf());
	if (arg.lower()==-iint::inf() && arg.upper()==iint::inf())	return Interval::empty();
	if (arg.lower()==-iint::inf() && arg.upper()!=iint::inf())	return Interval(arg.upper()+1, iint::inf());
	if (arg.lower()!=-iint::inf() && arg.upper()==iint::inf())	return Interval(-iint::inf(), arg.lower()-1);
	if (arg.lower()!=-iint::inf() && arg.upper()!=iint::inf())	return Interval(-iint::inf(), iint::inf()); // We lose precision here
	assert(false);
	return arg;
}

/**
 * Removes arg2 from arg1:		arg1 \ arg2
 */
Interval interval_remove(Interval arg1, Interval arg2) {
	if (boost::numeric::empty(arg1))		return arg1;	// Can not remove stuff from empty set
	if (boost::numeric::empty(arg2))		return arg1;	// Nothing to remove
	if (arg2.lower()==-iint::inf() && arg2.upper()==iint::inf()) return Interval::empty(); // Remove everything
	
	// We must only remove stuff from arg1 that arg1 and arg2 have incommon (ie. their intersection)
	Interval common = interval_intersection(arg1, arg2);
	if (boost::numeric::empty(common))		return arg1;	// arg2 only have stuff not in arg1; so nothing to remove from arg1
	
	Interval part1 = Interval(arg1.lower(), common.lower()-1);
	Interval part2 = Interval(common.upper()+1, arg1.upper());
	Interval ret = interval_union(part1, part2);
	return ret;
}
